﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;

using Microsoft.Reporting.WinForms;

using ERPStore.Extensions;

using CloudSoft.Extensions;

namespace ERPStore.Exporter.Reporting.Services
{
    public class ReportService
    {
        public static object m_lock = new object();

        public ReportService(ERPStore.Services.ICartService cartService
            ,ERPStore.Services.ICacheService cacheService
            , ERPStore.Services.ISalesService salesService
            , ERPStore.Services.IDocumentService documentService
            , ERPStore.Logging.ILogger logger
            )
        {
            this.CartService = cartService;
            this.Cache = cacheService;
            this.SalesService = salesService;
            this.DocumentService = documentService;
            this.Logger = logger;
        }

        protected ERPStore.Services.ICartService CartService { get; private set; }
        protected ERPStore.Services.ICacheService Cache { get; private set; }
        protected ERPStore.Services.ISalesService SalesService { get; private set; }
        protected ERPStore.Services.IDocumentService DocumentService { get; private set; }
        protected ERPStore.Logging.ILogger Logger { get; private set; }

        public string OrderCartReportFileName
        {
            get
            {
                return Configuration.ConfigurationSettings.AppSettings["orderCartReportFileName"]
                    ?? "ERPStore.Exporter.Reporting.OrderCart.rdlc, ERPStore.Exporter.Reporting";
            }
        }

        public string HeaderReportFileName
        {
            get
            {
                return Configuration.ConfigurationSettings.AppSettings["headerReportFileName"]
                    ?? "ERPStore.Exporter.Reporting.Header.rdlc, ERPStore.Exporter.Reporting";
            }
        }

        public byte[] GetOrderCartReportByCode(string cartCode, out string fileName, out string mimeType, ERPStore.Models.UserPrincipal principal, string format)
        {
            fileName = null;
            mimeType = null;
            var orderCart = CartService.GetCartByCode(cartCode);
            if (orderCart == null)
            {
                return null;
            }

            SalesService.ProcessExport(orderCart, principal);
            CartService.ApplyProductStockInfoList(orderCart);

            var parameters = new Dictionary<string, string>();
            parameters.Add("HeaderText" , string.Format("Devis N°CRT{0:000000} du {1:dd.MM.yyyy}", orderCart.Id, orderCart.CreationDate));
            parameters.Add("FooterText", string.Format("Devis N°CRT{0:000000} du {1:dd.MM.yyyy}", orderCart.Id, orderCart.CreationDate));
            var disclaimer = ERPStore.Exporter.Reporting.Configuration.ConfigurationSettings.AppSettings["orderCartDisclaimer"];
            parameters.Add("Disclaimer", disclaimer.Replace("||", System.Environment.NewLine));
            parameters.Add("DocumentTitle", "Devis");

            var report = CreateLocalReport("OrderCart", OrderCartReportFileName, parameters);

            var reportDataSource = new Models.OrderCartReport(orderCart);
            if (orderCart.BillingAddress != null)
            {
                reportDataSource.RecipientName = orderCart.BillingAddress.RecipientName;
                reportDataSource.Street = orderCart.BillingAddress.Street;
                reportDataSource.ZipCode = orderCart.BillingAddress.ZipCode;
                reportDataSource.City = orderCart.BillingAddress.City;
                reportDataSource.CountryName = orderCart.BillingAddress.Country.Name;
            }
            else if (principal.Identity.IsAuthenticated)
            {
                reportDataSource.RecipientName = principal.CurrentUser.DefaultAddress.RecipientName;
                reportDataSource.Street = principal.CurrentUser.DefaultAddress.Street;
                reportDataSource.ZipCode = principal.CurrentUser.DefaultAddress.ZipCode;
                reportDataSource.City = principal.CurrentUser.DefaultAddress.City;
                reportDataSource.CountryName = principal.CurrentUser.DefaultAddress.Country.Name;
            }

            AddReportDataSource(report, "OrderCart", reportDataSource);

            var cartItemListReport = new List<Models.CartItemReport>();
            foreach (var item in orderCart.Items)
            {
                var cartItem = new Models.CartItemReport(item);
                var imageUrl = cartItem.GetProductImageUrl();
                if (imageUrl != null)
                {
                    cartItem.ProductImageUrl = string.Format("http://{0}{1}", GlobalConfiguration.Configuration.Settings.DefaultUrl, string.Format(imageUrl, "0", "0"));
                }
                cartItem.ProductUrl = string.Format("http://{0}/produit/{1}/{2}", GlobalConfiguration.Configuration.Settings.DefaultUrl, cartItem.ProductCode, cartItem.GetProductUrl());
                cartItemListReport.Add(cartItem);
            }

            AddReportDataSource(report, "CartItem", cartItemListReport);

			var discountListReport = new List<Models.DiscountReport>();
			foreach (var discount in orderCart.DiscountList)
			{
				var item = new Models.DiscountReport();
				item.Code = discount.Code;
				item.CreationDate = discount.CreationDate;
				item.Discount = discount.Discount;
				item.DiscountReason = discount.DiscountReason;
				item.DiscountWithTax = discount.DiscountWithTax;
				discountListReport.Add(item);
			}

			AddReportDataSource(report, "Discount", discountListReport);

            byte[] result = null;
            string fileNameExtension = null;
            try
            {
                result = ExportTo(report, format, "<DeviceInfo></DeviceInfo>", out mimeType, out fileNameExtension);
                fileName = reportDataSource.CartCode + "." + fileNameExtension;
            }
            catch(Exception ex)
            {
                ex.Data.Add("Format", format);
                Logger.Error(ex);
            }

            return result;
        }

        private LocalReport CreateLocalReport(string displayName, string fileName, Dictionary<string, string> parameters)
        {
            var localReport = new LocalReport();
            localReport.DisplayName = displayName;
            localReport.EnableExternalImages = true;
            localReport.EnableHyperlinks = true;
            localReport.SubreportProcessing += (s, arg) =>
                {
                    foreach (string dataSourceName in arg.DataSourceNames)
                    {
                        if (localReport.DataSources[dataSourceName] != null)
                        {
                            arg.DataSources.Add(new Microsoft.Reporting.WinForms.ReportDataSource(dataSourceName, localReport.DataSources[dataSourceName].Value));
                        }
                    }
                };

            LoadReportDefinition(localReport,fileName );
            SetReportParameters(localReport, parameters);

            var agencySettings = new Models.AgencyReport(GlobalConfiguration.Configuration.Settings);
            AddReportDataSource(localReport, "Agency", agencySettings);

            LoadSubReportDefinition(localReport, HeaderReportFileName, "Header");

            return localReport;
        }


        private void LoadReportDefinition(LocalReport report, string fileName)
        {
            string key = string.Format("Report:{0}:{1}", report.DisplayName, fileName);
            string content = (string) Cache[key];
            if (content == null)
            {
                content = LoadReportContent(report, fileName);
                Cache.Add(key, content, DateTime.Now.AddHours(1));
            }

            using (TextReader reader = new StringReader(content))
            {
                report.LoadReportDefinition(reader);
                reader.Close();
            }
        }

        private void LoadSubReportDefinition(LocalReport report, string fileName, string subReportName)
        {
            string key = string.Format("SubReport:{0}:{1}", report.DisplayName, fileName);
            string content = (string)Cache[key];
            if (content == null)
            {
                content = LoadReportContent(report, fileName);
                Cache.Add(key, content, DateTime.Now.AddHours(1));
            }

            using (TextReader reader = new StringReader(content))
            {
                report.LoadSubreportDefinition(subReportName, reader);
                reader.Close();
            }
        }

        private string LoadReportContent(LocalReport report, string fileName)
        {
            var rdlcXmlContent = new System.Xml.XmlDocument();

            if (fileName.Contains(","))
            {
                var list = from token in fileName.Split(',')
                           select token;

                string assemblyName = list.LastOrDefault();
                if (assemblyName.IsNullOrTrimmedEmpty())
                {
                    return null;
                }

                var assembly = System.Reflection.Assembly.Load(assemblyName);
                if (assembly == null)
                {
                    return null;
                }

                using (System.IO.Stream stream = assembly.GetManifestResourceStream(list.First()))
                {
                    rdlcXmlContent.Load(stream);
                    stream.Close();
                }
            }
            else
            {
                rdlcXmlContent.Load(fileName);
            }

            return rdlcXmlContent.InnerXml;
        }

        protected void SetReportParameters(LocalReport report, Dictionary<string, string> parameters)
        {
            var list = report.GetParameters();
            var rpl = new List<ReportParameter>();
            foreach (var rpi in list)
            {
                ReportParameter rp = null;
                if (!parameters.ContainsKey(rpi.Name))
                {
                    rp = new ReportParameter(rpi.Name, " ** A DEFINIR (" + rpi.Name + ") ** ", true);
                }
                else
                {
                    string value = parameters[rpi.Name];
                    if (value == null)
                    {
                        value = string.Empty;
                    }
                    var prm = parameters[rpi.Name];
                    rp = new ReportParameter(rpi.Name, prm, true);
                }
                rpl.Add(rp);
            }
            report.SetParameters(rpl);
        }

        private byte[] ExportTo(LocalReport report, string format, string deviceInfo)
        {
            string mimeType = null;
            string fileNameExtension = null;
            return ExportTo(report, format, deviceInfo, out mimeType, out fileNameExtension);
        }

        private byte[] ExportTo(LocalReport report, string format, string deviceInfo, out string mimeType, out string fileNameExtension)
        {
            Warning[] warnings;
            string encoding = null;
            string[] streams;
            byte[] content = null;

            var formatList = report.ListRenderingExtensions();
			if (!formatList.Any(i =>format.Equals(i.Name, StringComparison.InvariantCultureIgnoreCase)))
			{
				format = "PDF";
			}

            // Methode render non thread safe
            lock (m_lock)
            {
                content = report.Render(format, deviceInfo, out mimeType, out encoding, out fileNameExtension, out streams, out warnings);
            }

            return content;
        }

        #region DataSource

        public void AddReportDataSource(LocalReport report, string datasourceName, object source)
        {
            var existing = report.DataSources.SingleOrDefault(i => i.Name.Equals(datasourceName, StringComparison.InvariantCultureIgnoreCase));
            if (existing != null)
            {
                return;
            }
            if (source is System.Collections.IEnumerable)
            {
                report.DataSources.Add(new ReportDataSource(datasourceName, (System.Collections.IEnumerable)source));
                return;
            }
            object[] list = new object[1];
            list[0] = source;
            report.DataSources.Add(new ReportDataSource(datasourceName, list));
        }

        #endregion

    }
}
